package io.github.lee.jel.global.di.http

import io.github.lee.jel.domain.po.JellyfinSession
import io.github.lee.jel.global.di.http.interceptor.JellyfinResponseInterceptor
import io.github.lee.jel.global.di.http.interceptor.PublicHeaderInterceptor
import io.github.lee.jel.global.di.http.interceptor.PublicParameterInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.security.GeneralSecurityException
import java.security.KeyStore
import java.util.Arrays
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager


private const val TIMEOUT = 5L
private val timeUnit = TimeUnit.SECONDS

object HttpModule {
    private var baseUrl = ""
    private val interceptorList = mutableListOf<Interceptor>()


    private fun systemDefaultTrustManager(): X509TrustManager {
        try {
            val trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm()
            )
            trustManagerFactory.init(null as KeyStore?)
            val trustManagers = trustManagerFactory.trustManagers
            if (trustManagers.size != 1 || trustManagers.get(0) !is X509TrustManager) {
                throw IllegalStateException(
                    "Unexpected default trust managers:"
                            + Arrays.toString(trustManagers)
                )
            }
            return trustManagers[0] as X509TrustManager
        } catch (e: GeneralSecurityException) {
            throw AssertionError() // The system has no TLS. Just give up.
        }
    }

    private fun sslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory {

        try {
            val sslContext = SSLContext.getInstance("TLS")
            sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
            return TLSSocketFactory(sslContext.socketFactory)
        } catch (e: GeneralSecurityException) {
            throw AssertionError() // The system has no TLS. Just give up.
        }
    }


    fun init(baseUrl: String, interceptorList: List<Interceptor>? = emptyList()) {
        HttpModule.baseUrl = baseUrl
        if (null != interceptorList) {
            HttpModule.interceptorList.clear()
            HttpModule.interceptorList.addAll(interceptorList)
        }
    }

    //    @Provides
    fun provideOkHttp(): OkHttpClient {
        val builder = OkHttpClient.Builder()
            .sslSocketFactory(SSLSocketClient.ssLSocketFactory, SSLSocketClient.trustManager[0])
            .hostnameVerifier(SSLSocketClient.hostnameVerifier)

            .connectTimeout(TIMEOUT, timeUnit)
            .callTimeout(TIMEOUT, timeUnit)
            .readTimeout(TIMEOUT, timeUnit)
            .writeTimeout(TIMEOUT, timeUnit)

//        val trustManager = systemDefaultTrustManager()
//        val factory = sslSocketFactory(trustManager)
//        builder.sslSocketFactory(factory, trustManager)

        if (interceptorList.isNotEmpty()) {
            interceptorList.forEach {
                builder.addInterceptor(it)
            }
        }
        return builder.build()
    }


    //    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }


    fun resetNetwork(session: JellyfinSession) {
        val list = mutableListOf<Interceptor>()
            .also {
                it.add(PublicHeaderInterceptor(session.username))
                it.add(PublicParameterInterceptor(session.accessToken))
                it.add(JellyfinResponseInterceptor())
                it.add(HttpLoggingInterceptor().also { interceptor ->
                    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
                }
                )
            }
        init(session.address, list)
    }

}